#!/usr/bin/env python
# encoding: utf-8

"""
@version: python3.7
@Author  : huangyi
@contact:  hy.dazui@qq.com
@Time    : 2020/1/13 1:03
@Describe: add Describe here
"""
from app.expression.nonterminal.and_expression import AndExpression
from app.expression.nonterminal.div_expression import DivExpression
from app.expression.nonterminal.multi_expression import MultiExpression
from app.expression.nonterminal.or_expression import OrExpression
from app.expression.nonterminal.plus_expression import PlusExpression
from app.expression.nonterminal.sub_expression import SubExpression
from app.expression.terminal.float_expression import FloatExpression
from app.expression.terminal.int_expression import IntExpression
from app.scanner.token.token import TokenType


class Ast:
    """
    构建语法树
    """

    def __init__(self):
        # 解释器栈
        self.expression_stack = []
        # 符号栈（包含运算符和括号）
        self.op_stack = []

        self.op_level = {
            "(": 1,
            ")": 100,
            "*": 2,
            "/": 2,
            "+": 3,
            "-": 3,
            "&&": 4,
            "||": 4
        }

    def parse(self, token_list: list):
        if token_list:
            for token in token_list:
                if token.get_type() in [TokenType.NUMBER, TokenType.POINT]:
                    expression = self.build_number_expression(token)
                    self.expression_stack.append(expression)
                else:
                    # 当前符号的优先级高于栈顶，或栈顶为空，则入栈
                    if not self.op_stack:
                        self.op_stack.append(token)
                    else:
                        if self.check_can_append(token):
                            # 优先级比栈顶高的直接入栈
                            self.op_stack.append(token)
                        else:
                            if token.get_type() != TokenType.RIGHT_BRACKET:
                                #  当前符号token入栈
                                if not self.check_can_append(token):
                                    self.build_tree_node(token)
                                self.op_stack.append(token)
                            else:
                                self.build_tree_node(token)

            # 最后只剩下一个解释器，就是语法树的根
            return self.expression_stack.pop()
        return None

    def build_tree_node(self, token):
        while self.op_stack and not self.check_can_append(token):
            # 弹出解释器栈两个解释器，构建一个非终结符解释器
            pop_token = self.op_stack.pop()
            if pop_token.get_type() == TokenType.LEFT_BRACKET:
                # 左括号直接弹出栈，不计算
                break
            expression1 = self.expression_stack.pop()
            expression2 = self.expression_stack.pop()
            # 栈的弹出顺序和表达式顺序相反
            expression = self.build_op_expression(pop_token, expression2, expression1)
            # 入解释器栈
            self.expression_stack.append(expression)

    def check_can_append(self, token):
        top_token = self.op_stack[-1]
        top_token_lv = self.op_level.get(top_token.get_data())
        current_token_lv = self.op_level.get(token.get_data(), 1000)
        return current_token_lv < top_token_lv

    def build_number_expression(self, token):
        expression = None
        if token.get_type() == TokenType.POINT:
            expression = FloatExpression(token.get_data())
        elif token.get_type() == TokenType.NUMBER:
            expression = IntExpression(token.get_data())
        else:
            raise Exception("unsupported token type: {}".format(token.get_type()))
        return expression

    def build_op_expression(self, token, expression1, expression2):
        expression = None
        if token.get_data() == "+":
            expression = PlusExpression(expression1, expression2)
        elif token.get_data() == "-":
            expression = SubExpression(expression1, expression2)
        elif token.get_data() == "*":
            expression = MultiExpression(expression1, expression2)
        elif token.get_data() == "/":
            expression = DivExpression(expression1, expression2)
        elif token.get_data() == "&&":
            expression = AndExpression(expression1, expression2)
        elif token.get_data() == "||":
            expression = OrExpression(expression1, expression2)
        else:
            raise Exception("unsupported op: {}".format(token.get_data()))
        return expression
